home *** CD-ROM | disk | FTP | other *** search
/ ShareWare OnLine 2 / ShareWare OnLine Volume 2 (CMS Software)(1993).iso / os2 / elvis172.zip / ctags.c < prev    next >
C/C++ Source or Header  |  1993-03-28  |  20KB  |  852 lines

  1. /* ctags.c */
  2.  
  3. /* This is a reimplementation of the ctags(1) program.  It supports ANSI C,
  4.  * and has heaps o' flags.  It is meant to be distributed with elvis.
  5.  */
  6.  
  7. #include <stdio.h>
  8. #ifdef __STDC__
  9. # include <string.h>
  10. # include <stdlib.h>
  11. #endif
  12. #include "config.h"
  13. #ifndef FALSE
  14. # define FALSE    0
  15. # define TRUE    1
  16. #endif
  17. #ifndef TAGS
  18. # define TAGS    "tags"
  19. #endif
  20. #ifndef REFS
  21. # define REFS    "refs"
  22. #endif
  23. #ifndef BLKSIZE
  24. # define BLKSIZE 1024
  25. #endif
  26.  
  27. #include "ctype.c" /* yes, that really is the .c file, not the .h one. */
  28.  
  29. extern void    file_open P_((char *));
  30. extern int    file_getc P_((void));
  31. extern void    file_ungetc P_((int));
  32. extern void    file_copyline P_((long, FILE *));
  33. extern void    cpp_open P_((char *));
  34. extern void    cpp_echo P_((int));
  35. extern int    cpp_getc P_((void));
  36. extern void    cpp_ungetc P_((int));
  37. extern int    lex_gettoken P_((void));
  38. extern void    maketag P_((int, long));
  39. extern void    ctags P_((char *));
  40. extern void    usage P_((void));
  41. extern void    main P_((int, char **));
  42.  
  43. extern char    **wildexpand P_((int *, char **));
  44. extern int    expand P_((char *));
  45. extern int    addfile P_((char *));
  46. extern int    pstrcmp P_((char **, char **));
  47.  
  48.  
  49. /* -------------------------------------------------------------------------- */
  50. /* Some global variables */
  51.  
  52. /* The following boolean variables are set according to command line flags */
  53. int    incl_static;    /* -s  include static tags */
  54. int    incl_types;    /* -t  include typedefs and structs */
  55. int    incl_vars;    /* -v  include variables */
  56. int    make_refs;    /* -r  generate a "refs" file */
  57. int    append_files;    /* -a  append to "tags" [and "refs"] files */
  58.  
  59. /* The following are used for outputting to the "tags" and "refs" files */
  60. FILE    *tags;        /* used for writing to the "tags" file */
  61. FILE    *refs;        /* used for writing to the "refs" file */
  62.  
  63. /* -------------------------------------------------------------------------- */
  64. /* These are used for reading a source file.  It keeps track of line numbers */
  65. char    *file_name;    /* name of the current file */
  66. FILE    *file_fp;    /* stream used for reading the file */
  67. long    file_lnum;    /* line number in the current file */
  68. long    file_seek;    /* fseek() offset to the start of current line */
  69. int    file_afternl;    /* boolean: was previous character a newline? */
  70. int    file_prevch;    /* a single character that was ungotten */
  71. int    file_header;    /* boolean: is the current file a header file? */
  72.  
  73. /* This function opens a file, and resets the line counter.  If it fails, it
  74.  * it will display an error message and leave the file_fp set to NULL.
  75.  */
  76. void file_open(name)
  77.     char    *name;    /* name of file to be opened */
  78. {
  79.     /* if another file was already open, then close it */
  80.     if (file_fp)
  81.     {
  82.         fclose(file_fp);
  83.     }
  84.  
  85.     /* try to open the file for reading.  The file must be opened in
  86.      * "binary" mode because otherwise fseek() would misbehave under DOS.
  87.      */
  88. #if MSDOS || TOS || OS2
  89.     file_fp = fopen(name, "rb");
  90. #else
  91.     file_fp = fopen(name, "r");
  92. #endif
  93.     if (!file_fp)
  94.     {
  95.         perror(name);
  96.     }
  97.  
  98.     /* reset the name & line number */
  99.     file_name = name;
  100.     file_lnum = 0L;
  101.     file_seek = 0L;
  102.     file_afternl = TRUE;
  103.  
  104.     /* determine whether this is a header file */
  105.     file_header = FALSE;
  106.     name += strlen(name) - 2;
  107.     if (name >= file_name && name[0] == '.' && (name[1] == 'h' || name[1] == 'H'))
  108.     {
  109.         file_header = TRUE;
  110.     }
  111. }
  112.  
  113. /* This function reads a single character from the stream.  If the *previous*
  114.  * character was a newline, then it also increments file_lnum and sets
  115.  * file_offset.
  116.  */
  117. int file_getc()
  118. {
  119.     int    ch;
  120.  
  121.     /* if there is an ungotten character, then return it.  Don't do any
  122.      * other processing on it, though, because we already did that the
  123.      * first time it was read.
  124.      */
  125.     if (file_prevch)
  126.     {
  127.         ch = file_prevch;
  128.         file_prevch = 0;
  129.         return ch;
  130.     }
  131.  
  132.     /* if previous character was a newline, then we're starting a line */
  133.     if (file_afternl && file_fp)
  134.     {
  135.         file_afternl = FALSE;
  136.         file_seek = ftell(file_fp);
  137.         file_lnum++;
  138.     }
  139.  
  140.     /* Get a character.  If no file is open, then return EOF */
  141.     ch = (file_fp ? getc(file_fp) : EOF);
  142.  
  143.     /* if it is a newline, then remember that fact */
  144.     if (ch == '\n')
  145.     {
  146.         file_afternl = TRUE;
  147.     }
  148.  
  149.     /* return the character */
  150.     return ch;
  151. }
  152.  
  153. /* This function ungets a character from the current source file */
  154. void file_ungetc(ch)
  155.     int    ch;    /* character to be ungotten */
  156. {
  157.     file_prevch = ch;
  158. }
  159.  
  160. /* This function copies the current line out some other fp.  It has no effect
  161.  * on the file_getc() function.  During copying, any '\' characters are doubled
  162.  * and a leading '^' or trailing '$' is also quoted.  The '\n' character is not
  163.  * copied.  If the '\n' is preceded by a '\r', then the '\r' isn't copied.
  164.  *
  165.  * This is meant to be used when generating a tag line.
  166.  */
  167. void file_copyline(seek, fp)
  168.     long    seek;    /* where the lines starts in the source file */
  169.     FILE    *fp;    /* the output stream to copy it to */
  170. {
  171.     long    oldseek;/* where the file's pointer was before we messed it up */
  172.     char    ch;    /* a single character from the file */
  173.     char    next;    /* the next character from this file */
  174.  
  175.     /* go to the start of the line */
  176.     oldseek = ftell(file_fp);
  177.     fseek(file_fp, seek, 0);
  178.  
  179.     /* if first character is '^', then emit \^ */
  180.     ch = getc(file_fp);
  181.     if (ch == '^')
  182.     {
  183.         putc('\\', fp);
  184.         putc('^', fp);
  185.         ch = getc(file_fp);
  186.     }
  187.  
  188.     /* write everything up to, but not including, the newline */
  189.     while (ch != '\n')
  190.     {
  191.         /* preread the next character from this file */
  192.         next = getc(file_fp);
  193.  
  194.         /* if character is '\', or a terminal '$', then quote it */
  195.         if (ch == '\\' || (ch == '$' && next == '\n'))
  196.         {
  197.             putc('\\', fp);
  198.         }
  199.  
  200.         /* copy the character, unless it is a terminal '\r' */
  201.         if (ch != '\r' || next != '\n')
  202.             putc(ch, fp);
  203.  
  204.         /* next character... */
  205.         ch = next;
  206.     }
  207.  
  208.     /* seek back to the old position */
  209.     fseek(file_fp, oldseek, 0);
  210. }
  211.  
  212. /* -------------------------------------------------------------------------- */
  213. /* This section handles preprocessor directives.  It strips out all of the
  214.  * directives, and may emit a tag for #define directives.
  215.  */
  216.  
  217. int    cpp_afternl;    /* boolean: look for '#' character? */
  218. int    cpp_prevch;    /* an ungotten character, if any */
  219. int    cpp_refsok;    /* boolean: can we echo characters out to "refs"? */
  220.  
  221. /* This function opens the file & resets variables */
  222. void cpp_open(name)
  223.     char    *name;    /* name of source file to be opened */
  224. {
  225.     /* use the lower-level file_open function to open the file */
  226.     file_open(name);
  227.  
  228.     /* reset variables */
  229.     cpp_afternl = TRUE;
  230.     cpp_refsok = TRUE;
  231. }
  232.  
  233. /* This function copies a character from the source file to the "refs" file */
  234. void cpp_echo(ch)
  235.     int    ch; /* the character to copy */
  236. {
  237.     static    wasnl;
  238.  
  239.     /* echo non-EOF chars, unless not making "ref", or echo turned off */
  240.     if (ch != EOF && make_refs && cpp_refsok && !file_header)
  241.     {
  242.         /* try to avoid blank lines */
  243.         if (ch == '\n')
  244.         {
  245.             if (wasnl)
  246.             {
  247.                 return;
  248.             }
  249.             wasnl = TRUE;
  250.         }
  251.         else
  252.         {
  253.             wasnl = FALSE;
  254.         }
  255.  
  256.         /* add the character */
  257.         putc(ch, refs);
  258.     }
  259. }
  260.  
  261. /* This function returns the next character which isn't part of a directive */
  262. int cpp_getc()
  263. {
  264.     static
  265.     int    ch;    /* the next input character */
  266.     char    *scan;
  267.  
  268.     /* if we have an ungotten character, then return it */
  269.     if (cpp_prevch)
  270.     {
  271.         ch = cpp_prevch;
  272.         cpp_prevch = 0;
  273.         return ch;
  274.     }
  275.  
  276.     /* Get a character from the file.  Return it if not special '#' */
  277.     ch = file_getc();
  278.     if (ch == '\n')
  279.     {
  280.         cpp_afternl = TRUE;
  281.         cpp_echo(ch);
  282.         return ch;
  283.     }
  284.     else if (ch != '#' || !cpp_afternl)
  285.     {
  286.         /* normal character.  Any non-whitespace should turn off afternl */
  287.         if (ch != ' ' && ch != '\t')
  288.         {
  289.             cpp_afternl = FALSE;
  290.         }
  291.         cpp_echo(ch);
  292.         return ch;
  293.     }
  294.  
  295.     /* Yikes!  We found a directive */
  296.  
  297.     /* see whether this is a #define line */
  298.     scan = " define ";
  299.     while (*scan)
  300.     {
  301.         if (*scan == ' ')
  302.         {
  303.             /* space character matches any whitespace */
  304.             do
  305.             {
  306.                 ch = file_getc();
  307.             } while (ch == ' ' || ch == '\t');
  308.             file_ungetc(ch);
  309.         }
  310.         else
  311.         {
  312.             /* other characters should match exactly */
  313.             ch = file_getc();
  314.             if (ch != *scan)
  315.             {
  316.                 file_ungetc(ch);
  317.                 break;
  318.             }
  319.         }
  320.         scan++;
  321.     }
  322.  
  323.     /* is this a #define line?  and should we generate a tag for it? */
  324.     if (!*scan && (file_header || incl_static))
  325.     {
  326.         /* if not a header, then this will be a static tag */
  327.         if (!file_header)
  328.         {
  329.             fputs(file_name, tags);
  330.             putc(':', tags);
  331.         }
  332.  
  333.         /* output the tag name */
  334.         for (ch = file_getc(); isalnum(ch) || ch == '_'; ch = file_getc())
  335.         {
  336.             putc(ch, tags);
  337.         }
  338.  
  339.         /* output a tab, the filename, another tab, and the line number */
  340.         fprintf(tags, "\t%s\t%ld\n", file_name, file_lnum);
  341.     }
  342.  
  343.     /* skip to the end of the directive -- a newline that isn't preceded
  344.      * by a '\' character.
  345.      */
  346.     while (ch != EOF && ch != '\n')
  347.     {
  348.         if (ch == '\\')
  349.         {
  350.             ch = file_getc();
  351.         }
  352.         ch = file_getc();
  353.     }
  354.  
  355.     /* return the newline that we found at the end of the directive */
  356.     cpp_echo(ch);
  357.     return ch;
  358. }
  359.  
  360. void
  361. /* This puts a character back into the input queue for the source file */
  362. cpp_ungetc(ch)
  363.     int    ch;    /* a character to be ungotten */
  364. {
  365.     cpp_prevch = ch;
  366. }
  367.  
  368.  
  369. /* -------------------------------------------------------------------------- */
  370. /* This is the lexical analyser.  It gets characters from the preprocessor,
  371.  * and gives tokens to the parser.  Some special codes are...
  372.  *   (deleted)  / *...* / (comments)
  373.  *   (deleted)    //...\n    (comments)
  374.  *   (deleted)    (*    (parens used in complex declaration)
  375.  *   (deleted)    [...]    (array subscript, when ... contains no ])
  376.  *   (deleted)    struct    (intro to structure declaration)
  377.  *   BODY    {...}    ('{' can occur anywhere, '}' only at BOW if ... has '{')
  378.  *   ARGS    (...{    (args of function, not extern or forward)
  379.  *   ARGS    (...);    (args of an extern/forward function declaration)
  380.  *   COMMA    ,    (separate declarations that have same scope)
  381.  *   SEMICOLON    ;    (separate declarations that have different scope)
  382.  *   SEMICOLON  =...;    (initializer)
  383.  *   TYPEDEF    typedef    (the "typedef" keyword)
  384.  *   KSTATIC    static    (the "static" keyword)
  385.  *   KSTATIC    private    (the "static" keyword)
  386.  *   KSTATIC    PRIVATE    (the "static" keyword)
  387.  *   NAME    [a-z]+    (really any valid name that isn't reserved word)
  388.  */
  389.  
  390. /* #define EOF -1 */
  391. #define DELETED      0
  392. #define BODY      1
  393. #define ARGS      2
  394. #define COMMA      3
  395. #define SEMICOLON 4
  396. #define TYPEDEF   5
  397. #define KSTATIC      6
  398. #define EXTERN      7
  399. #define NAME      8
  400.  
  401. char    lex_name[BLKSIZE];    /* the name of a "NAME" token */
  402. long    lex_seek;        /* start of line that contains lex_name */
  403.  
  404. int
  405. lex_gettoken()
  406. {
  407.     int    ch;        /* a character from the preprocessor */
  408.     int    next;        /* the next character */
  409.     int    token;        /* the token that we'll return */
  410.     int    i;
  411.  
  412.     /* loop until we get a token that isn't "DELETED" */
  413.     do
  414.     {
  415.         /* get the next character */
  416.         ch = cpp_getc();
  417.  
  418.         /* process the character */
  419.         switch (ch)
  420.         {
  421.           case ',':
  422.             token = COMMA;
  423.             break;
  424.  
  425.           case ';':
  426.             token = SEMICOLON;
  427.             break;
  428.  
  429.           case '/':
  430.             /* get the next character */
  431.             ch = cpp_getc();
  432.             switch (ch)
  433.             {
  434.               case '*':    /* start of C comment */
  435.                 ch = cpp_getc();
  436.                 next = cpp_getc();
  437.                 while (next != EOF && (ch != '*' || next != '/'))
  438.                 {
  439.                     ch = next;
  440.                     next = cpp_getc();
  441.                 }
  442.                 break;
  443.  
  444.               case '/':    /* start of a C++ comment */
  445.                 do
  446.                 {
  447.                     ch = cpp_getc();
  448.                 } while (ch != '\n' && ch != EOF);
  449.                 break;
  450.  
  451.               default:    /* some other slash */
  452.                 cpp_ungetc(ch);
  453.             }
  454.             token = DELETED;
  455.             break;
  456.  
  457.           case '(':
  458.             ch = cpp_getc();
  459.             if (ch == '*')
  460.             {
  461.                 token = DELETED;
  462.             }
  463.             else
  464.             {
  465.                 next = cpp_getc();
  466.                 while (ch != '{' && ch != EOF && (ch != ')' || next != ';'))/*}*/
  467.                 {
  468.                     ch = next;
  469.                     next = cpp_getc();
  470.                 }
  471.                 if (ch == '{')/*}*/
  472.                 {
  473.                     cpp_ungetc(ch);
  474.                 }
  475.                 else if (next == ';')
  476.                 {
  477.                     cpp_ungetc(next);
  478.                 }
  479.                 token = ARGS;
  480.             }
  481.             break;
  482.  
  483.           case '{':/*}*/
  484.             /* don't send the next characters to "refs" */
  485.             cpp_refsok = FALSE;
  486.  
  487.             /* skip ahead to closing '}', or to embedded '{' */
  488.             do
  489.             {
  490.                 ch = cpp_getc();
  491.             } while (ch != '{' && ch != '}' && ch != EOF);
  492.  
  493.             /* if has embedded '{', then skip to '}' in column 1 */
  494.             if (ch == '{') /*}*/
  495.             {
  496.                 ch = cpp_getc();
  497.                 next = cpp_getc();
  498.                 while (ch != EOF && (ch != '\n' || next != '}'))/*{*/
  499.                 {
  500.                     ch = next;
  501.                     next = cpp_getc();
  502.                 }
  503.             }
  504.  
  505.             /* resume "refs" processing */
  506.             cpp_refsok = TRUE;
  507.             cpp_echo('}');
  508.  
  509.             token = BODY;
  510.             break;
  511.  
  512.           case '[':
  513.             /* skip to matching ']' */
  514.             do
  515.             {
  516.                 ch = cpp_getc();
  517.             } while (ch != ']' && ch != EOF);
  518.             token = DELETED;
  519.             break;
  520.  
  521.           case '=':
  522.               /* skip to next ';' */
  523.             do
  524.             {
  525.                 ch = cpp_getc();
  526.  
  527.                 /* leave array initializers out of "refs" */
  528.                 if (ch == '{')
  529.                 {
  530.                     cpp_refsok = FALSE;
  531.                 }
  532.             } while (ch != ';' && ch != EOF);
  533.  
  534.             /* resume echoing to "refs" */
  535.             if (!cpp_refsok)
  536.             {
  537.                 cpp_refsok = TRUE;
  538.                 cpp_echo('}');
  539.                 cpp_echo(';');
  540.             }
  541.             token = SEMICOLON;
  542.             break;
  543.  
  544.           case EOF:
  545.             token = EOF;
  546.             break;
  547.  
  548.           default:
  549.             /* is this the start of a name/keyword? */
  550.             if (isalpha(ch) || ch == '_')
  551.             {
  552.                 /* collect the whole word */
  553.                 lex_name[0] = ch;
  554.                 for (i = 1, ch = cpp_getc();
  555.                      i < BLKSIZE - 1 && (isalnum(ch) || ch == '_');
  556.                      i++, ch = cpp_getc())
  557.                 {
  558.                     lex_name[i] = ch;
  559.                 }
  560.                 lex_name[i] = '\0';
  561.                 cpp_ungetc(ch);
  562.  
  563.                 /* is it a reserved word? */
  564.                 if (!strcmp(lex_name, "typedef"))
  565.                 {
  566.                     token = TYPEDEF;
  567.                     lex_seek = -1L;
  568.                 }
  569.                 else if (!strcmp(lex_name, "static")
  570.                       || !strcmp(lex_name, "private")
  571.                       || !strcmp(lex_name, "PRIVATE"))
  572.                 {
  573.                     token = KSTATIC;
  574.                     lex_seek = -1L;
  575.                 }
  576.                 else if (!strcmp(lex_name, "extern")
  577.                       || !strcmp(lex_name, "EXTERN")
  578.                       || !strcmp(lex_name, "FORWARD"))
  579.                 {
  580.                     token = EXTERN;
  581.                     lex_seek = -1L;
  582.                 }
  583.                 else
  584.                 {
  585.                     token = NAME;
  586.                     lex_seek = file_seek;
  587.                 }
  588.             }
  589.             else /* not part of a name/keyword */
  590.             {
  591.                 token = DELETED;
  592.             }
  593.  
  594.         } /* end switch(ch) */
  595.  
  596.     } while (token == DELETED);
  597.  
  598.     return token;
  599. }
  600.  
  601. /* -------------------------------------------------------------------------- */
  602. /* This is the parser.  It locates tag candidates, and then decides whether to
  603.  * generate a tag for them.
  604.  */
  605.  
  606. /* This function generates a tag for the object in lex_name, whose tag line is
  607.  * located at a given seek offset.
  608.  */
  609. void maketag(scope, seek)
  610.     int    scope;    /* 0 if global, or KSTATIC if static */
  611.     long    seek;    /* the seek offset of the line */
  612. {
  613.     /* output the tagname and filename fields */
  614.     if (scope == EXTERN)
  615.     {
  616.         /* whoa!  we should *never* output a tag for "extern" decl */
  617.         return;
  618.     }
  619.     else if (scope == KSTATIC)
  620.     {
  621.         fprintf(tags, "%s:%s\t%s\t", file_name, lex_name, file_name);
  622.     }
  623.     else
  624.     {
  625.         fprintf(tags, "%s\t%s\t", lex_name, file_name);
  626.     }
  627.  
  628.     /* output the target line */
  629.     putc('/', tags);
  630.     putc('^', tags);
  631.     file_copyline(seek, tags);
  632.     putc('$', tags);
  633.     putc('/', tags);
  634.     putc('\n', tags);
  635. }
  636.  
  637.  
  638. /* This function parses a source file, adding any tags that it finds */
  639. void ctags(name)
  640.     char    *name;    /* the name of a source file to be checked */
  641. {
  642.     int    prev;    /* the previous token from the source file */
  643.     int    token;    /* the current token from the source file */
  644.     int    scope;    /* normally 0, but could be a TYPEDEF or KSTATIC token */
  645.     int    gotname;/* boolean: does lex_name contain a tag candidate? */
  646.     long    tagseek;/* start of line that contains lex_name */
  647.  
  648.     /* open the file */
  649.     cpp_open(name);
  650.  
  651.     /* reset */
  652.     scope = 0;
  653.     gotname = FALSE;
  654.     token = SEMICOLON;
  655.  
  656.     /* parse until the end of the file */
  657.     while (prev = token, (token = lex_gettoken()) != EOF)
  658.     {
  659.         /* scope keyword? */
  660.         if (token == TYPEDEF || token == KSTATIC || token == EXTERN)
  661.         {
  662.             scope = token;
  663.             gotname = FALSE;
  664.             continue;
  665.         }
  666.  
  667.         /* name of a possible tag candidate? */
  668.         if (token == NAME)
  669.         {
  670.             tagseek = file_seek;
  671.             gotname = TRUE;
  672.             continue;
  673.         }
  674.  
  675.         /* if NAME BODY, without ARGS, then NAME is a struct tag */
  676.         if (gotname && token == BODY && prev != ARGS)
  677.         {
  678.             gotname = FALSE;
  679.             
  680.             /* ignore if in typedef -- better name is coming soon */
  681.             if (scope == TYPEDEF)
  682.             {
  683.                 continue;
  684.             }
  685.  
  686.             /* generate a tag, if -t and maybe -s */
  687.             if (incl_types && (file_header || incl_static))
  688.             {
  689.                 maketag(file_header ? 0 : KSTATIC, tagseek);
  690.             }
  691.         }
  692.  
  693.         /* If NAME ARGS BODY, then NAME is a function */
  694.         if (gotname && prev == ARGS && token == BODY)
  695.         {
  696.             gotname = FALSE;
  697.             
  698.             /* generate a tag, maybe checking -s */
  699.             if (scope != KSTATIC || incl_static)
  700.             {
  701.                 maketag(scope, tagseek);
  702.             }
  703.         }
  704.  
  705.         /* If NAME SEMICOLON or NAME COMMA, then NAME is var/typedef */
  706.         if (gotname && (token == SEMICOLON || token == COMMA))
  707.         {
  708.             gotname = FALSE;
  709.  
  710.             /* generate a tag, if -v/-t and maybe -s */
  711.             if (scope == TYPEDEF && incl_types && (file_header || incl_static)
  712.              || scope == KSTATIC && incl_vars && incl_static
  713.              || incl_vars)
  714.             {
  715.                 /* a TYPEDEF outside of a header is KSTATIC */
  716.                 if (scope == TYPEDEF && !file_header)
  717.                 {
  718.                     maketag(KSTATIC, tagseek);
  719.                 }
  720.                 else /* use whatever scope was declared */
  721.                 {
  722.                     maketag(scope, tagseek);
  723.                 }
  724.             }
  725.         }
  726.  
  727.         /* reset after a semicolon or ARGS BODY pair */
  728.         if (token == SEMICOLON || (prev == ARGS && token == BODY))
  729.         {
  730.             scope = 0;
  731.             gotname = FALSE;
  732.         }
  733.     }
  734.  
  735.     /* The source file will be automatically closed */
  736. }
  737.  
  738. /* -------------------------------------------------------------------------- */
  739.  
  740. void usage()
  741. {
  742.     fprintf(stderr, "usage: ctags [flags] filenames...\n");
  743.     fprintf(stderr, "\t-s  include static functions\n");
  744.     fprintf(stderr, "\t-t  include typedefs\n");
  745.     fprintf(stderr, "\t-v  include variable declarations\n");
  746.     fprintf(stderr, "\t-r  generate a \"refs\" file, too\n");
  747.     fprintf(stderr, "\t-a  append to \"tags\", instead of overwriting\n");
  748.     exit(2);
  749. }
  750.  
  751.  
  752.  
  753. #if AMIGA
  754. # include "amiwild.c"
  755. #endif
  756.  
  757. #if VMS
  758. # include "vmswild.c"
  759. #endif
  760.  
  761. void
  762. main(argc, argv)
  763.     int    argc;
  764.     char    **argv;
  765. {
  766.     int    i, j;
  767. #if MSDOS || TOS || OS2
  768.     char    **wildexpand();
  769. #endif
  770.  
  771.     /* build the tables used by the ctype macros */
  772.     _ct_init((uchar *)"");
  773.  
  774. #if MSDOS || TOS || OS2
  775.     argv = wildexpand(&argc, argv);
  776. #endif
  777.  
  778.     /* parse the option flags */
  779.     for (i = 1; i < argc && argv[i][0] == '-'; i++)
  780.     {
  781.         for (j = 1; argv[i][j]; j++)
  782.         {
  783.             switch (argv[i][j])
  784.             {
  785.               case 's':    incl_static = TRUE;    break;
  786.               case 't':    incl_types = TRUE;    break;
  787.               case 'v':    incl_vars = TRUE;    break;
  788.               case 'r':    make_refs = TRUE;    break;
  789.               case 'a':    append_files = TRUE;    break;
  790.               default:    usage();
  791.             }
  792.         }
  793.     }
  794.  
  795.     /* There should always be at least one source file named in args */
  796.     if (i == argc)
  797.     {
  798.         usage();
  799.     }
  800.  
  801.     /* open the "tags" and maybe "refs" files */
  802.     tags = fopen(TAGS, append_files ? "a" : "w");
  803.     if (!tags)
  804.     {
  805.         perror(TAGS);
  806.         exit(3);
  807.     }
  808.     if (make_refs)
  809.     {
  810.         refs = fopen(REFS, append_files ? "a" : "w");
  811.         if (!refs)
  812.         {
  813.             perror(REFS);
  814.             exit(4);
  815.         }
  816.     }
  817.  
  818.     /* parse each source file */
  819.     for (; i < argc; i++)
  820.     {
  821.         ctags(argv[i]);
  822.     }
  823.  
  824.     /* close "tags" and maybe "refs" */
  825.     fclose(tags);
  826.     if (make_refs)
  827.     {
  828.         fclose(refs);
  829.     }
  830.  
  831. #ifdef SORT
  832.         /* This is a hack which will sort the tags list.   It should
  833.          * on UNIX and OS-9.  You may have trouble with csh.   Note
  834.          * that the tags list only has to be sorted if you intend to
  835.          * use it with the real vi;  elvis permits unsorted tags.
  836.          */
  837. # if OSK
  838.         system("qsort tags >-_tags; -nx; del tags; rename _tags tags");
  839. # else    
  840.         system("sort tags >_tags$$; mv _tags$$ tags");
  841. # endif
  842. #endif
  843.  
  844.     exit(0);
  845.     /*NOTREACHED*/
  846. }
  847.  
  848. #if MSDOS || TOS || OS2
  849. # define WILDCARD_NO_MAIN
  850. # include "wildcard.c"
  851. #endif
  852.